using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;

namespace Pechovic.CommandLine {
    
    /// <summary>
    /// 
    /// </summary>
    public abstract class InputParams {

        /// <summary>
        /// Exception that will be thrown if the error occurs.
        /// </summary>
        private Type exception;

        /// <summary>
        /// Ctor.
        /// </summary>
        /// <param name="ex">Exception that will be thrown if the error occurs.</param>
        public InputParams(Type ex) {
            exception = ex;
            PropertyInfo[] pInfos = this.GetType().GetProperties();

            infos = new List<BindingInfo>();
            foreach (PropertyInfo pi in pInfos) {
                object[] attr = pi.GetCustomAttributes(typeof(InputParamAttribute), true);
                if (attr.Length == 1) {
                    infos.Add(new BindingInfo(pi, (InputParamAttribute)attr[0]));
                }
            }
        }

        private List<BindingInfo> infos;

        public void Fill(string[] args) {

            for (int i = 0; i < infos.Count; i++) {
                try {
                    if (infos[i].Attribute.IsRequired || ((args.Length - i)  > 0)) {
                        infos[i].Pi.SetValue(this,
                            Convert.ChangeType(args[i], infos[i].Pi.PropertyType), null);
                    } else {
                        infos[i].Pi.SetValue(this,
                            Convert.ChangeType(infos[i].Attribute.DefaultValue, infos[i].Pi.PropertyType), null);
                    }
                } catch (Exception ex) {
                    throw CreateException("Wrong input parameters.", ex);
                }
            }
        }

        public void CheckIfFileExist(string path) {
            if (!File.Exists(path))
                throw CreateException("File '" + path + "' doesn't exist.");
        }

        private Exception CreateException() {
            return (Exception)Activator.CreateInstance(exception);
        }

        private Exception CreateException(string message) {
            return (Exception)Activator.CreateInstance(exception, message);
        }

        private Exception CreateException(string message, Exception innerException) {
            return (Exception)Activator.CreateInstance(exception, message, innerException);
        }

        private struct BindingInfo {
            public PropertyInfo Pi;
            public InputParamAttribute Attribute;

            public BindingInfo(PropertyInfo pi, InputParamAttribute attribute) {
                Pi = pi;
                Attribute = attribute;
            }
        }


        public string CreateHelpText() {
            StringBuilder sb = new StringBuilder();
            BindingInfo info;
            for (int i = 0; i < infos.Count; i++) {
                info = infos[i];
                sb.AppendFormat("{2}{0}... {1}{4}{3}{5}", i + 1, info.Attribute.HelpText,
                    info.Attribute.IsRequired ? string.Empty : "[",
                    info.Attribute.IsRequired ? string.Empty : "]",
                    info.Attribute.IsRequired ? string.Empty :
                    string.Format("Default value='{0}'", info.Attribute.DefaultValue),
                    Environment.NewLine);
            }
            sb.AppendLine();
            return sb.ToString();
        }

    }

    [AttributeUsage(AttributeTargets.Property)]
    public class InputParamAttribute : Attribute {
        private bool isRequired = true;
        private object defaultValue;
        private string helpText = string.Empty;

        public bool IsRequired {
            get { return isRequired; }
            set { isRequired = value; }
        }

        public object DefaultValue {
            get { return defaultValue; }
            set { defaultValue = value; }
        }

        public string HelpText {
            get { return helpText; }
            set { helpText = value; }
        }
    }

    
}
